// #pragma comment(lib, "../fixapi/fixapi50.lib")
#include "DDRequest.h"

#include "IndividualConfig.h"
#include "AsyncMsg.h"

#include <spdlog/async.h>

// static const std::string dd_addr = "58.210.86.53@1827/tcp"s;
static const std::string dd_addr = "219.146.84.187@9919/tcp"s;
// static const std::string dd_addr = "27.223.31.46@9919/tcp"s;
const char OperatorId[9] = "99999999";
const char DepartmentId[5] = "9999";
const char DstDepartmentId[5] = "9999";
const char DDCertFile[] = "citics_a5_client_test.pfx";
const char DDCertPwd[] = "600030";

const char Account[] = "676246";
char Password[64] = "600000";
string SHOptionAccount;
string SHSubOptionAccount;

string FundAccountId;

std::shared_ptr<spdlog::logger> logger = nullptr;

FixInfo dd_fix_info{
	"5",
	"HD:IP=127.0.0.1",
	0, 0, ""};

AsyncMsg *g_AsyncMsg = nullptr;
AsyncMsg *g_AsyncMsg2 = nullptr;

HANDLE_CONN Connect()
{
	HANDLE_CONN hConn = Fix_Connect(dd_addr.c_str(), "", "", 5);
	if (hConn == 0)
	{
		fmt::print(fg(fmt::color::red), "与柜台建立连接失败\n");
		return 0;
	}

	return hConn;
}

void PrintAnswers(const vector<map<int, string>> &answer)
{
	for (const auto &item : answer)
	{
		for (const auto &[k, v] : item)
		{
			fmt::print(fg(fmt::color::alice_blue), "({}：{})", k, v);
		}

		fmt::print("\n");
	}
}

string_view GetValueById(const map<int, string> &record, int id)
{
	auto it = record.find(id);
	if (it == record.end())
		return "";
	return it->second;
}

bool Login(HANDLE_CONN hconn)
{
	DDRequest login(hconn);

	login.Init("100001", dd_fix_info);

	login.AddParam(FID_KHH, Account);
	login.AddParam(FID_JMLX, "5");

	char pwd[256]{};
	strcpy(pwd, Password);
	int len = sizeof(pwd);
	Fix_UniEncode(Account, pwd, len);
	if (pwd[0] == '\0')
	{
		fmt::print("密码加密失败\n");
		return false;
	}
	login.AddParam(FID_JYMM, pwd);

	bool ret = login.SendAndRecv();
	if (!ret)
		return false;

	auto answer = login.GetAnswers();

	assert(answer.size() == 1);

	dd_fix_info.nodeId = atoi(GetValueById(answer[0], FID_NODEID).data());
	dd_fix_info.sysId = atoi(GetValueById(answer[0], FID_YWLX).data());
	dd_fix_info.token = GetValueById(answer[0], FID_TOKEN);

	auto name = GetValueById(answer[0], FID_KHXM);
	fmt::print("name type = {}\n", typeid(name).name());
	auto res = GetValueById(answer[0], FID_NOTE);

	// Fix_SubscribeByCustomer()

	return true;
}

bool LoginEx(HANDLE_CONN hconn)
{
	DDRequest login(hconn);

	login.Init("100015", dd_fix_info);

	login.AddParam(FID_TYPE, "1");
	login.AddParam(FID_CONTENT, Account);
	login.AddParam(FID_JMLX, "6");

	char pwd[256]{};
	strcpy(pwd, Password);
	int len = sizeof(pwd);
	Fix_UniEncode3rd(Account, pwd, len);
	if (pwd[0] == '\0')
	{
		fmt::print("密码加密失败\n");
		return false;
	}
	login.AddParam(FID_JYMM, pwd);

	bool ret = login.SendAndRecv();
	if (!ret)
		return false;

	auto answer = login.GetAnswers();

	dd_fix_info.nodeId = atoi(GetValueById(answer[0], FID_NODEID).data());
	dd_fix_info.sysId = atoi(GetValueById(answer[0], FID_YWLX).data());
	dd_fix_info.token = GetValueById(answer[0], FID_TOKEN);

	auto clientId = GetValueById(answer[0], FID_KHH);
	auto name = GetValueById(answer[0], FID_KHXM);
	auto lastIpMac = GetValueById(answer[0], FID_NOTE);
	auto nodeRegion = GetValueById(answer[0], FID_QYBM);

	fmt::print("({}:{})({}:{})({}:{})({}:{})({}:{})\n",
			   FID_NODEID, dd_fix_info.nodeId,
			   FID_YWLX, dd_fix_info.sysId,
			   FID_TOKEN, dd_fix_info.token,
			   FID_KHXM, name,
			   FID_QYBM, nodeRegion);

	return true;
}

void QueryAccount(HANDLE_CONN hconn)
{
	DDRequest query(hconn);

	query.Init("710005", dd_fix_info);

	query.AddParam(FID_KHH, Account);

	if (!query.SendAndRecv())
	{
		return;
	}

	auto answer = query.GetAnswers();
	for (const auto &item : answer)
	{
		auto market = GetValueById(item, FID_JYS);
		if (market == "SH")
		{
			SHOptionAccount = GetValueById(item, FID_GDH);
			SHSubOptionAccount = GetValueById(item, FID_ZZHBM);
			break;
		}
	}
}

void DoSubPush(HANDLE_CONN hconn)
{
	{
		HANDLE_SESSION sess = Fix_AllocateSession(hconn);
		Fix_SetWTFS(sess, dd_fix_info.wtfs.c_str());
		Fix_SetNode(sess, dd_fix_info.node.c_str());
		Fix_SetToken(sess, dd_fix_info.token.c_str());
		Fix_SetSystemId(sess, dd_fix_info.sysId);
		Fix_SetNodeId(sess, dd_fix_info.nodeId);
		Fix_CreateHead(sess, "JM0003");

		SubPushOrder(sess, Account, dd_fix_info.token.c_str());
		Fix_ReleaseSession(sess);
	}

	{
		HANDLE_SESSION sess = Fix_AllocateSession(hconn);
		Fix_SetWTFS(sess, dd_fix_info.wtfs.c_str());
		Fix_SetNode(sess, dd_fix_info.node.c_str());
		Fix_SetToken(sess, dd_fix_info.token.c_str());
		Fix_SetSystemId(sess, dd_fix_info.sysId);
		Fix_SetNodeId(sess, dd_fix_info.nodeId);
		Fix_CreateHead(sess, "JM0004");
		SubPushTrade(sess, Account, dd_fix_info.token.c_str());
		Fix_ReleaseSession(sess);
	}
}

void QueryFund(HANDLE_CONN hconn)
{
	{
		DDRequest query(hconn);
		query.Init("200001", dd_fix_info);

		query.AddParam(FID_KHH, Account);

		if (!query.SendAndRecv())
			return;

		auto answer = query.GetAnswers();
		PrintAnswers(answer);

		FundAccountId = answer[0][FID_ZJZH];
	}

	{
		DDRequest query_2(hconn);
		query_2.Init("200002", dd_fix_info);

		query_2.AddParam(FID_KHH, Account);
		query_2.AddParam(FID_ZJZH, FundAccountId);

		if (!query_2.SendAndRecv())
			return;

		auto answer_2 = query_2.GetAnswers();
		PrintAnswers(answer_2);
	}
}

void InsertOrder(HANDLE_CONN hconn)
{
	DDRequest order(hconn);

	order.Init("711000", dd_fix_info);

	order.AddParam(FID_KHH, Account);
	order.AddParam(FID_GDH, SHOptionAccount);
	order.AddParam(FID_JYS, "SH");
	order.AddParam(FID_ZZHBM, SHSubOptionAccount);
	order.AddParam(FID_QQHYDM, "10003538");
	order.AddParam(FID_MMFX, "1");	 // 买
	order.AddParam(FID_KPBZ, "O");	 // 开
	order.AddParam(FID_QQBDBQ, "0"); // 非备兑
	order.AddParam(FID_DDLX, "2");
	order.AddParam(FID_WTSL, "1");
	order.AddParam(FID_WTJG, "0.034");
	order.AddParam(FID_FQF, "1");

	if (!order.SendAndRecv())
		return;

	auto answer = order.GetAnswers();
	PrintAnswers(answer);
}

void QueryOptionContracts(HANDLE_CONN hconn)
{
	DDRequest query(hconn);

	query.Init("700000", dd_fix_info);
	query.AddParam(FID_JYS, "SH");
	query.AddParam(FID_ROWCOUNT, "200");
	query.AddParam(FID_BROWINDEX, "");

	bool ret = query.SendAndRecv();
	if (!ret)
		return;

	auto answer = query.GetAnswers();
	fmt::print("查询合约返回结果如下:\n");
	PrintAnswers(answer);
}

void DoProcess(HANDLE_CONN conn)
{
	// if (Login(conn))
	if (LoginEx(conn))
	{
		fmt::print(fg(fmt::color::alice_blue), "LoginEx suucess..\n");
		// DoSubPush(conn);
		// QueryFund(conn);
		// QueryAccount(conn);
		// InsertOrder(conn);
		//  QueryOptionContracts(conn);
	}
}

void PrepareBeforeConnect()
{
	// g_IndividualConfig.Init();
	spdlog::init_thread_pool(1024 * 128, 1);
	logger = spdlog::rotating_logger_mt("DDTest", "logs/rotating_log.txt", 1024 * 1024 * 10, 3);

	// 全局初始化DLL一次
	Fix_Initialize();

	Fix_SetCharset("UTF-8");

	Fix_SetCertFile(DDCertFile, DDCertPwd);

	Fix_SetDefaultInfo(OperatorId, "", DepartmentId, DstDepartmentId);
}

void TestSync(HANDLE_CONN conn)
{
	try
	{
		for (int i = 0; i < 1; ++i)
		{
			// thread(DoProcess, conn).detach();
			DoProcess(conn);
		}
	}
	catch (const spdlog::spdlog_ex &ex)
	{
		std::cout << "Log init failed: " << ex.what() << std::endl;
	}
}

void AsyncTest(AsyncMsg *&asyncMsg, HANDLE_CONN conn, int type)
{
	asyncMsg = new AsyncMsg(Account, Password, conn);

	asyncMsg->SetCallback(type);
	asyncMsg->AsyncLoginEx();

	std::unique_lock<std::mutex> lock(asyncMsg->m_Mtx);
	asyncMsg->m_Cv.wait(lock);
	asyncMsg->AsyncQueryStockAccount();
	asyncMsg->AsyncQueryRiskInfo();
}

int main(int argc, char *argv[])
{
	PrepareBeforeConnect();

	HANDLE_CONN conn = Connect();
	if (conn <= 0)
	{
		fmt::print("Connect failed\n");
		exit(1);
	}
	fmt::print("连接句柄:{}\n", conn);

	if (argc < 2)
	{
		TestSync(conn);
	}
	else
	{
		fmt::print("Main thread id = {}\n", ::syscall(SYS_gettid));

		HANDLE_CONN conn2 = Connect();
		if (conn <= 0)
		{
			fmt::print("Connect2 failed\n");
		}

		AsyncTest(g_AsyncMsg, conn, 1);
		AsyncTest(g_AsyncMsg2, conn2, 2);
	}

	getchar();
	// 全局退出DLL一次
	Fix_Uninitialize();

	return 0;
}